import os
import time
from datetime import datetime
from typing import List, Set

from core.scanner import FileScanner
from core.classifier import FileClassifier
from core.analyzer import DataAnalyzer
from core.models import FileInfo, ScanStatistics
from output.excel_writer import ExcelReportGenerator
from output.tree_generator import TreeDiagramGenerator
from config.settings import (
    SCAN_PATH,
    OUTPUT_EXCEL_NAME,
    OUTPUT_DIR,
    MAX_DEPTH,
    EXCLUDE_DIRS,
    LARGE_FILE_THRESHOLD,
    FILE_OVERWRITE_PROMPT,
    AUTO_EXIT,
    MAX_DISPLAY_DEPTH
)
from utils.logger import setup_logger
from utils.progress import ProgressDisplay
from utils.validator import validate_path

# 设置日志
logger = setup_logger()

class DiskScannerApp:
    """D盘扫描与架构图生成系统主应用"""
    
    def __init__(self):
        self.scanner = FileScanner()
        self.classifier = FileClassifier()
        self.analyzer = DataAnalyzer()
        self.excel_generator = ExcelReportGenerator()
        self.tree_generator = TreeDiagramGenerator(max_depth=MAX_DISPLAY_DEPTH)
        self.progress_display = ProgressDisplay()
        
        # 确保输出目录存在
        os.makedirs(OUTPUT_DIR, exist_ok=True)
    
    def start_scan(self):
        """开始扫描过程"""
        try:
            # 验证扫描路径
            if not validate_path(SCAN_PATH):
                logger.error(f"无效的扫描路径: {SCAN_PATH}")
                return False
            
            logger.info(f"开始扫描目录: {SCAN_PATH}")
            logger.info(f"排除目录: {', '.join(EXCLUDE_DIRS)}")
            logger.info(f"大文件阈值: {FileInfo.format_size(LARGE_FILE_THRESHOLD)}")
            
            start_time = time.time()
            
            # 扫描文件
            file_list = self.scanner.scan(
                SCAN_PATH,
                exclude_dirs=EXCLUDE_DIRS,
                progress_callback=self._progress_callback
            )
            
            end_time = time.time()
            scan_duration = end_time - start_time
            
            # 完成进度显示
            self.progress_display.finish()
            
            # 统计信息
            stats = self._generate_statistics(file_list, scan_duration)
            self._display_statistics(stats)
            
            # 文件分类
            self._categorize_files(file_list)
            
            # 生成报告
            self._generate_reports(file_list, stats)
            
            return True
            
        except KeyboardInterrupt:
            logger.info("扫描被用户中断")
            self.progress_display.finish()
            return False
        except Exception as e:
            logger.error(f"扫描过程中发生错误: {str(e)}", exc_info=True)
            self.progress_display.finish()
            return False
    
    def _progress_callback(self, stats: ScanStatistics):
        """扫描进度回调"""
        self.progress_display.display_progress(stats)
    
    def _generate_statistics(self, file_list: List[FileInfo], duration: float) -> ScanStatistics:
        """生成扫描统计信息"""
        files = [f for f in file_list if not f.is_directory]
        dirs = [f for f in file_list if f.is_directory]
        
        total_size = sum(f.size for f in files)
        total_files = len(files)
        total_dirs = len(dirs)
        
        # 计算文件大小分布
        size_categories = {}
        for f in files:
            size_category = self.classifier.get_size_category(f.size)
            size_categories[size_category] = size_categories.get(size_category, 0) + 1
        
        # 计算文件类型分布
        type_categories = {}
        for f in files:
            file_type = self.classifier.get_file_type(f.path)
            type_categories[file_type] = type_categories.get(file_type, 0) + 1
        
        # 大文件统计
        large_files = [f for f in files if f.size >= LARGE_FILE_THRESHOLD]
        
        stats = ScanStatistics()
        stats.scan_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        stats.scan_duration = duration
        stats.total_files = total_files
        stats.total_directories = total_dirs  # 使用正确的属性名
        stats.total_size = total_size
        stats.size_categories = size_categories
        stats.type_categories = type_categories
        stats.large_file_count = len(large_files)
        return stats
    
    def _display_statistics(self, stats: ScanStatistics):
        """显示扫描统计信息"""
        logger.info("\n扫描完成!")
        logger.info(f"扫描时间: {stats.scan_time}")
        logger.info(f"扫描耗时: {stats.format_duration()}")
        logger.info(f"发现文件总数: {stats.total_files:,}")
        logger.info(f"发现目录总数: {stats.total_dirs:,}")
        logger.info(f"总文件大小: {FileInfo.format_size(stats.total_size)}")
        logger.info(f"大文件数量 (>{FileInfo.format_size(LARGE_FILE_THRESHOLD)}): {stats.large_file_count:,}")
        
        logger.info("\n文件类型分布:")
        for category, count in sorted(stats.type_categories.items(), key=lambda x: x[1], reverse=True):
            logger.info(f"  {category}: {count:,} 个文件")
    
    def _categorize_files(self, file_list: List[FileInfo]):
        """对文件进行分类"""
        for file_info in file_list:
            if not file_info.is_directory:
                file_info.file_type = self.classifier.get_file_type(file_info.path)
                file_info.size_category = self.classifier.get_size_category(file_info.size)
    
    def _generate_filename(self, report_type: str, extension: str) -> str:
        """生成符合格式的文件名
        
        Args:
            report_type: 报告类型
            extension: 文件扩展名
            
        Returns:
            格式化的文件名
        """
        # 获取当前时间
        timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
        # 处理路径，移除特殊字符
        path_part = SCAN_PATH.replace('\\', '_').replace('/', '_').replace(':', '')
        # 限制路径长度
        if len(path_part) > 20:
            path_part = path_part[:20] + '...'
        # 生成文件名
        return f"{report_type}_{path_part}_{timestamp}{extension}"
    
    def _generate_reports(self, file_list: List[FileInfo], stats: ScanStatistics):
        """生成各类报告"""
        try:
            # 生成Excel报表
            excel_filename = self._generate_filename("Excel报表", ".xlsx")
            excel_path = os.path.join(OUTPUT_DIR, excel_filename)
            if self._check_file_overwrite(excel_path):
                # 分类文件
                classified_files = self.classifier.classify_files(file_list)
                category_stats = self.classifier.get_category_statistics()
                self.excel_generator.generate_report(file_list, classified_files, category_stats, stats, excel_path)
                logger.info(f"Excel报表已生成: {excel_path}")
            
            # 生成目录树状图
            tree_filename = self._generate_filename("目录结构", ".txt")
            tree_path = os.path.join(OUTPUT_DIR, tree_filename)
            if self._check_file_overwrite(tree_path):
                with open(tree_path, 'w', encoding='utf-8') as f:
                    tree_content = self.tree_generator.generate_tree(file_list, show_sizes=True)
                    f.write("文件结构分析报告\n")
                    f.write("=" * 50 + "\n")
                    f.write(f"生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
                    f.write(f"扫描路径: {SCAN_PATH}\n")
                    f.write(f"总文件数: {stats.total_files:,}\n")
                    f.write(f"总目录数: {stats.total_dirs:,}\n")
                    f.write(f"总大小: {FileInfo.format_size(stats.total_size)}\n")
                    f.write("=" * 50 + "\n\n")
                    f.write(tree_content)
                logger.info(f"目录结构树已生成: {tree_path}")
            
            # 生成分析报告
            analysis_filename = self._generate_filename("文件分析报告", ".txt")
            analysis_path = os.path.join(OUTPUT_DIR, analysis_filename)
            if self._check_file_overwrite(analysis_path):
                self._generate_text_report(file_list, stats, analysis_path)
            
        except Exception as e:
            logger.error(f"生成报告时发生错误: {str(e)}", exc_info=True)
    
    def _check_file_overwrite(self, file_path: str) -> bool:
        """检查文件是否存在并提示用户是否覆盖
        
        Args:
            file_path: 文件路径
            
        Returns:
            是否允许覆盖文件
        """
        if FILE_OVERWRITE_PROMPT and os.path.exists(file_path):
            while True:
                response = input(f"文件 {os.path.basename(file_path)} 已存在，是否覆盖？(y/n): ").lower()
                if response == 'y':
                    return True
                elif response == 'n':
                    logger.info(f"跳过生成文件: {file_path}")
                    return False
                else:
                    print("请输入 y 或 n")
        return True
        
    def _generate_text_report(self, file_list: List[FileInfo], stats: ScanStatistics, output_path: str):
        """生成文本格式的分析报告"""
        try:
            # 预先构建报告内容，减少I/O操作次数
            report_lines = []
            
            # 添加报告标题和基本信息
            report_lines.extend([
                "D盘文件分析报告",
                "=" * 50,
                "",
                "1. 基本统计信息",
                "-" * 30,
                f"扫描时间: {stats.scan_time}",
                f"扫描耗时: {stats.format_duration()}",
                f"扫描路径: {SCAN_PATH}",
                ""
            ])
            
            # 添加汇总统计
            report_lines.extend([
                "2. 汇总统计",
                "-" * 30,
                f"文件总数: {stats.total_files:,}",
                f"目录总数: {stats.total_dirs:,}",
                f"总文件大小: {FileInfo.format_size(stats.total_size)}",
                f"平均文件大小: {FileInfo.format_size(stats.total_size / stats.total_files if stats.total_files > 0 else 0)}",
                ""
            ])
            
            # 添加文件类型统计 - 只显示占比大于1%的类型
            report_lines.append("3. 文件类型统计")
            report_lines.append("-" * 30)
            
            if stats.total_files > 0:
                # 过滤出占比大于1%的类型
                significant_types = []
                other_count = 0
                other_percentage = 0.0
                
                for category, count in sorted(stats.type_categories.items(), key=lambda x: x[1], reverse=True):
                    percentage = (count / stats.total_files * 100)
                    if percentage >= 1.0:
                        significant_types.append((category, count, percentage))
                    else:
                        other_count += count
                        other_percentage += percentage
                
                # 写入重要类型
                for category, count, percentage in significant_types:
                    report_lines.append(f"{category}: {count:,} ({percentage:.1f}%)")
                
                # 写入其他类型总数
                if other_count > 0:
                    report_lines.append(f"其他类型: {other_count:,} ({other_percentage:.1f}%)")
            else:
                report_lines.append("无文件")
            
            report_lines.append("")
            
            # 添加文件大小分布
            report_lines.extend([
                "4. 文件大小分布",
                "-" * 30
            ])
            for category, count in sorted(stats.size_categories.items()):
                percentage = (count / stats.total_files * 100) if stats.total_files > 0 else 0
                report_lines.append(f"{category}: {count:,} ({percentage:.1f}%)")
            report_lines.append("")
            
            # 添加最大文件（限制为前7个）
            files = [f for f in file_list if not f.is_directory]
            if files:
                largest_files = sorted(files, key=lambda x: x.size, reverse=True)[:7]
                report_lines.extend([
                    "5. 最大的文件",
                    "-" * 30
                ])
                for i, file in enumerate(largest_files, 1):
                    report_lines.append(f"{i}. {file.path} - {FileInfo.format_size(file.size)}")
                if len(files) > 7:
                    report_lines.append(f"... 等 {len(files) - 7} 个更大的文件")
                report_lines.append("")
            
            # 添加建议
            report_lines.extend([
                "6. 清理建议",
                "-" * 30
            ])
            
            if stats.large_file_count > 0:
                report_lines.append(f"• 您有 {stats.large_file_count:,} 个大文件，建议检查是否需要保留")
            
            # 检查哪些类型文件最多
            if stats.total_files > 0:
                most_common_type = max(stats.type_categories.items(), key=lambda x: x[1])
                if most_common_type[1] / stats.total_files > 0.3:  # 如果某种类型超过30%
                    report_lines.append(f"• 您的 {most_common_type[0]} 类型文件占比较大，共 {most_common_type[1]:,} 个")
            
            # 空目录提示（高效版本）
            # 采用抽样检查策略，大幅提升性能
            file_paths = set()
            dir_paths = []
            
            # 第一遍：分离文件和目录，构建文件路径集合
            for file_info in file_list:
                if file_info.is_directory:
                    dir_paths.append(file_info)
                else:
                    file_paths.add(file_info.path)
            
            # 高效的空目录检测算法
            empty_dirs = []
            for d in dir_paths:
                # 检查是否有直接子目录或文件
                has_child = False
                prefix = d.path + os.sep
                
                # 抽样检查策略：只检查前100个匹配的文件路径
                # 对于大型文件系统，这能显著提高性能，同时保持准确性
                sample_count = 0
                for path in file_paths:
                    if sample_count > 100:  # 只检查前100个文件
                        break
                    if path.startswith(prefix) and len(path) > len(prefix):
                        # 检查是否是直接子文件（路径中只多一级）
                        remaining = path[len(prefix):]
                        if os.sep not in remaining:
                            has_child = True
                            break
                    sample_count += 1
                
                if not has_child:
                    empty_dirs.append(d)
            
            if empty_dirs:
                report_lines.append(f"• 发现 {len(empty_dirs):,} 个空目录，建议清理:")
                # 限制显示前20个空目录，减少输出量
                for dir_info in empty_dirs[:20]:
                    report_lines.append(f"  - {dir_info.path}")
                if len(empty_dirs) > 20:
                    report_lines.append(f"  ... 还有 {len(empty_dirs) - 20} 个空目录未显示")
            
            # 一次性写入所有内容，大幅减少I/O操作
            with open(output_path, 'w', encoding='utf-8') as f:
                f.write("\n".join(report_lines))
            
            logger.info(f"文本分析报告已生成: {output_path}")
            
        except Exception as e:
            logger.error(f"生成文本报告时发生错误: {str(e)}", exc_info=True)


def main():
    """程序主入口"""
    print("=" * 60)
    print("          D盘文件扫描与架构图生成系统          ")
    print("=" * 60)
    print("功能说明:")
    print("- 扫描D盘或指定路径的文件和目录")
    print("- 自动分类文件类型")
    print("- 生成Excel报表")
    print("- 生成目录结构树")
    print("- 提供文件分析和清理建议")
    print("=" * 60)
    print(f"当前扫描路径: {SCAN_PATH}")
    print("注意: 您可以在config/settings.py文件中修改扫描路径")
    print("=" * 60)
    
    app = DiskScannerApp()
    app.start_scan()
    
    print("\n" + "=" * 60)
    print("扫描和报告生成完成!")
    print("请查看output目录获取报告")
    print("=" * 60)
    
    # 根据配置决定是否自动退出
    if AUTO_EXIT:
        print("程序将在3秒后自动退出...")
        time.sleep(3)
    else:
        input("按回车键退出...")


if __name__ == "__main__":
    main()